home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Scheduling / Cassandra / Source / Cassandra.m < prev    next >
Encoding:
Text File  |  1992-11-09  |  27.0 KB  |  1,193 lines

  1. //
  2. // Cassandra.m
  3. // Copyright (c) 1988, 1989, 1990, 1991, 1992 by Jiro Nakamura 
  4. // All rights reserved
  5. //
  6. // Main control object of Cassandra. Routes most calls to their proper places
  7. // and handles alarm control.
  8. //
  9. //    by Jiro Nakamura (jiro@shaman.com)
  10. //
  11. // RCS Information
  12. // Revision Number->    $Revision: 2.19 $
  13. // Last Revised->    $Date: 92/11/10 00:50:10 $
  14. //
  15. static char authorid[] = "$Author: jiro $";
  16. static char rcsid[] = "$Id: Cassandra.m,v 2.19 92/11/10 00:50:10 jiro Exp Locker: jiro $";
  17. static char copyrightid[] = 
  18.     "Copyright (C) 1988, 1989, 1990, 1991 by Jiro Nakamura. "
  19.     "All Rights Reserved.";
  20.         
  21. #import <stdio.h>
  22. #import <libc.h>
  23. #import <time.h>        
  24. #import <sys/file.h>
  25. #import <strings.h>
  26. #import <appkit/Form.h>
  27. #import <appkit/Button.h>
  28. #import <appkit/Panel.h>           /* for NXRunAlertPanel()     */
  29. #import <appkit/publicWraps.h>  /* for NXBeep( )         */
  30. #import <appkit/Application.h>     /* for NX_MODALRESPTHRESHOLD      */
  31. #import <appkit/PageLayout.h>
  32. #import <dpsclient/wraps.h>    
  33. #import <soundkit/Sound.h>
  34. #import <bsd/dev/m68k/evsio.h>    /* For screen handling */
  35.  
  36. #import "Cassandra.h"
  37. #import "Event.h"
  38. #import "Global.h"
  39. #import "misc.h"
  40. #import "calendar.h"
  41. #import "EventLog.h"
  42. #import "Notepad.h"
  43. #import "Overview.h"
  44. #import "Today.h"
  45.  
  46. #define    WHENREADY    -1.0        // for startAlarmTimedEntry
  47. #define    NOW        0.0
  48.  
  49. #define MAXSECONDS    (24 * 60 * 60)        // 24 hours in seconds
  50.  
  51. char *getlogin();        // This isn't defined anywhere....
  52.                 // NeXT, get on your feet!
  53.  
  54. @implementation Cassandra
  55.  
  56. // We override new in order to set the delegate of the application object
  57. // to itself. This allows the appDidInit: and appDidHide: delegate methods
  58. // to be invoked properly.
  59. + new
  60. {
  61.  
  62.     #ifdef DEBUG
  63.         fprintf(stderr,"Cassandra being created.\n");
  64.     #endif
  65.       
  66.     self = [super new];
  67.     [self setDelegate:self];
  68.  
  69.     alarmFree = TRUE;
  70.     snooze = 0;
  71.     secondsUntilEvent = 0.0;
  72.     queueModified = TRUE;
  73.     return self;
  74. }
  75.  
  76. // appDidInit is called as the first thing in the run loop of the 
  77. // application. At this point, everything is created, but we haven't entered
  78. // the event loop yet. appDidInit first loads the global variables in
  79. // from the defaults database, then appDidInit creates a new instance 
  80. // Clock, and
  81. // makes it a subview of the application icon window (obtained through
  82. // the appIcon method of Application). It then goes about and sets the
  83. // lockFile for cassandra, opening the windows we need, etc.
  84. - appDidInit: app
  85. {
  86.     FILE *lFile;                // lockFile file descriptor
  87.     char line1[127], tmpFile[129];
  88.     char tmp[256];
  89.     NXRect cvFrame;                // contentView frame
  90.     id applicationIcon = [NXApp appIcon];    // Our beloved icon
  91.  
  92.     #ifdef DEBUG
  93.         fprintf(stderr,"Cassandra did initialize."
  94.                 "    (From Cassandra.m)\n");
  95.     #endif
  96.     
  97.     now = *timeNow();        // Set the time
  98.     [global    load:self];        // Let's load the global variables
  99.     
  100.     queueModified = YES;        // Make sure queue gets re-read
  101.     
  102.     // Check for lockfile, if none, create one. If one, ask user
  103.     // what he/she wants to do.
  104.     strcpy(lockFile,[global eventFile]);    // "eventFile" + ".lck"
  105.     strcat(lockFile,".lck");
  106.     if( access( lockFile, F_OK ) == 0)    // if it exists
  107.         {
  108.         lFile = fopen(lockFile,"r");
  109.         fgets(line1,128,lFile);
  110.         fclose(lFile);
  111.         
  112.         if( NXRunAlertPanel("Event File in Use",line1,
  113.                 "Quit","Open Anyway",NULL) == 1)
  114.             exit(1);
  115.         }
  116.     
  117.     // Ok. None exists. Create the lockFile giving the current
  118.     // username and time.
  119.     lFile = fopen( lockFile, "w");
  120.     fprintf(lFile,    "The Event file is still in use. "
  121.             "It was opened by %s on %s.\n"
  122.             "Do you want to open it anyway?", 
  123.             NXUserName(),
  124.             ascMyTime( (struct tm *)timeNow(), 
  125.             [global showSeconds], 
  126.             [global militaryTime]));
  127.     fclose(lFile);
  128.  
  129.     // Check to see if there is a compressed EventFile
  130.     strcpy( tmpFile, [global eventFile]);
  131.     strcat( tmpFile, ".Z");
  132.     if( access([global eventFile], R_OK) != 0 &&
  133.         access( tmpFile, R_OK) == 0)    // It exists
  134.         {
  135.         sprintf( tmp, "/usr/ucb/uncompress %s", tmpFile);
  136.         #ifdef DEBUG
  137.             fprintf( stderr, "%s:", PROGNAME, tmp);
  138.         #endif
  139.         system( tmp);
  140.         }
  141.  
  142.     // Set up the clock icon
  143.     // This is from ClockApp (used w/ author's permission)
  144.     [[applicationIcon contentView] getFrame:&cvFrame];
  145.     NXInsetRect(&cvFrame, 3.0, 3.0);    //let 's not touch the border
  146.     clockView = [Clock newFrame:&cvFrame];
  147.  
  148.     [[applicationIcon contentView] addSubview:clockView];
  149.     [self defaultsDidChange:self];
  150.     [applicationIcon display];     
  151.     // then, set the target and action of Clock so we can use it.
  152.     [clockView setTarget:    self];
  153.     [clockView setAction:    @selector(tick:)];
  154.     [self updateClockViewMessage: self];
  155.     
  156.     
  157.     // If we should source .cassandra/login at login-time
  158.     if( [global    sourceLoginOnLaunch] && [global autoLaunched])
  159.         {
  160.         sprintf(tmp, "/bin/sh %s",[global sourceLoginFile] );
  161.         #ifdef DEBUG
  162.             fprintf( stderr, "%s:", PROGNAME, tmp);
  163.         #endif
  164.         system( tmp );
  165.         }
  166.         
  167.     
  168.     // If we are opening the Overview at launch
  169.     if( [global    overviewOnLaunch])    
  170.         [overview open: self];
  171.  
  172.     // If we are opening the Log at launch
  173.     if( [global    eventLogOnLaunch])    
  174.         [eventLog open:self];
  175.         
  176.     // If we are opening the Notepad at launch    
  177.     if( [global    notepadOnLaunch])    
  178.         [notepad open:self];
  179.         
  180.     // If we are opening Today at launch
  181.     if( [global    todayOnLaunch])    
  182.         [today open:self];
  183.     
  184.     // If we are opening Week at launch
  185.     if( [global    weekOnLaunch])    
  186.         [week open:self];
  187.  
  188.     [self startAlarmTimedEntry: NOW];
  189.  
  190.     // If we should hide ourselves at launch...
  191.     if( [global    hideOnLaunch] && [global autoLaunched] )
  192.         [NXApp    perform:    @selector(hide:)
  193.             with:        self
  194.             afterDelay:    1
  195.             cancelPrevious:    NO];
  196.  
  197.     return self;
  198. }
  199.  
  200. //
  201. // appDidHide is called if the user hides the application.
  202. // appDidUnhide is called when the user unhides the application.
  203. // These aren't implemented (just yet).
  204. //
  205. - appDidHide
  206. {
  207.  
  208.     return self;
  209. }
  210.  
  211. - appDidUnhide
  212. {
  213.     [overview     timeUpdate:self];
  214.     [today        timeUpdate: self];
  215.     [week        timeUpdate: self];
  216.     
  217.     if(  timeNow()->tm_mday != now.tm_mday)
  218.         {
  219.         [today update];
  220.         [overview monthUpdate: self];
  221.         now = *timeNow();
  222.         }
  223.     return self;
  224. }
  225.  
  226. //
  227. // Cassandra is about to quit. First, remove the lockfile,
  228. // the close all the windows (they will save their positions
  229. // automatically) and then quit.
  230. //
  231. - cassandraWillQuit:sender;
  232. {
  233.     char tmp[256];
  234.     
  235.     unlink( lockFile);    // Remove lockFile before quiting
  236.     
  237.     if( [global    compressEvents])
  238.         {
  239.         sprintf( tmp, "/usr/ucb/compress -f %s&", [global eventFile]);
  240.         #ifdef DEBUG
  241.             fprintf(stderr,"Compressing event file...\n");
  242.             fprintf(stderr, "%s:", PROGNAME, tmp);
  243.         #endif
  244.  
  245.         system( tmp);
  246.         }
  247.  
  248.     // If we should source .cassandra/logout at logout-time
  249.     if( [global    sourceLoginOnLaunch]  && [global autoLaunched] )
  250.         {
  251.         sprintf(tmp, "/bin/sh %s",[global sourceLogoutFile] );
  252.         #ifdef DEBUG
  253.             fprintf( stderr, "%s:", PROGNAME, tmp);
  254.         #endif
  255.         system( tmp );
  256.         }
  257.                     
  258.     if( [overview        isVisible])
  259.         [overview     performClose:self];
  260.     if( [global        isVisible])
  261.         [global     performClose:self];
  262.  
  263.     if( [notepad        isVisible])
  264.         [notepad     performClose:self];
  265.     if( [eventLog        isVisible])
  266.         [eventLog     performClose:self];
  267.     if( [today         isVisible])
  268.         [today         performClose: self];
  269.     if( [week         isVisible])
  270.         [week         performClose: self];
  271.     if( [addEventPanel    isVisible])
  272.         [addEventPanel    performClose: self];
  273.     if( [editEventPanel    isVisible])
  274.         [editEventPanel    performClose: self];
  275.     
  276.     return self;
  277. }
  278.  
  279. //
  280. // We want to catch the powerOff and logOut messages from the WM
  281. // The user should manually quit Cassandra or else window
  282. // positions will NOT be saved.
  283. //
  284. - appPowerOffIn: (int) ms andSave: (int) aFlag
  285. {
  286.     // We are powering off, oh no....
  287.     if( aFlag == YES)    // If we are allowed to save, then
  288.         [self cassandraWillQuit: self];
  289.     else
  290.         unlink( lockFile);    // At least remove the lockFile 
  291.                     // before quiting
  292.     
  293.     return self;
  294. }    
  295.  
  296. //
  297. // Brings up the information panel.
  298. //
  299. - info:sender
  300. {
  301.     if (!infoPanel) 
  302.         {
  303.     infoPanel=[self loadNibSection:"InfoPanel.nib" owner:self withNames:NO];
  304.         }
  305.     [infoPanel orderFront:self];
  306.     return self;
  307. }
  308.  
  309. //
  310. // Brings up the help panel.
  311. //
  312. - help:sender
  313. {
  314.     if (!helpPanel) 
  315.         {
  316.     helpPanel = [self loadNibSection:"HelpPanel.nib" owner:self withNames:NO];
  317.         }
  318.     [helpPanel orderFront:self];
  319.     return self;
  320. }
  321.  
  322. //
  323. // Brings up the Calculator window.
  324. //
  325. - calculator:sender
  326. {
  327.     if( calculatorWindow == nil)
  328.         {
  329.         calculatorWindow  = [self loadNibSection: "Calculator.nib" 
  330.             owner:self withNames:NO];
  331.             }
  332.     [calculatorWindow makeKeyAndOrderFront:self];
  333.     return self;
  334. }
  335.  
  336.  
  337. // quit is called by the quit button on the main menu.
  338. // checks to see if the user really wants to quit, then invokes
  339. // the terminate method of itself if she or he does.
  340. - quit: sender
  341. {
  342.  
  343.     #ifdef ASK_IF_QUITTING
  344.         if( NXRunAlertPanel(NULL,"Really quit?",
  345.             "Quit","Cancel",NULL) == 1)
  346.             {
  347.             [self        cassandraWillQuit:self];
  348.             [NXApp         terminate: self];
  349.             }
  350.     #else
  351.         [self        cassandraWillQuit:self];
  352.         [NXApp         terminate: self];
  353.     #endif
  354.         
  355.     return nil;
  356. }
  357.  
  358. // closeKeyWindow: is called by the Close button on the Window submenu
  359. // it closes the key window (naturally).
  360. - closeKeyWindow:sender
  361. {
  362.     [[self keyWindow] performClose:self];
  363.     return self;
  364. }
  365.  
  366. // saveKeyWindow: is called by the Save button on the Window submenu
  367. // it saves the key window (naturally).
  368. - saveKeyWindow:sender
  369. {
  370.     if( [[self keyWindow] respondsTo: @selector(save)])
  371.         [[self keyWindow] save];
  372.     return self;
  373. }
  374.  
  375.  
  376. // miniaturizeKeyWindow: is called by the Minituarize button on the 
  377. // Window submenu, it miniaturizes the key window (naturally).
  378. - miniaturizeKeyWindow:sender
  379. {
  380.     [[self keyWindow] performMiniaturize:self];
  381.     return self;
  382. }
  383.  
  384. // Runs the page layout panel
  385. - runPageLayout: sender
  386. {
  387.     [[PageLayout new] runModal];
  388.     return self;
  389. }
  390.  
  391. - print: sender
  392. {
  393.     id mainWin = [NXApp keyWindow];
  394.  
  395.     if( mainWin == nil )
  396.         NXBeep();
  397.     
  398.     return [mainWin  printPSCode: sender];
  399. }
  400.  
  401.  
  402. // =======================================================
  403. //              Alarm Routines
  404. //========================================================
  405. // Ideally, these should be in a separate class.
  406. // But efficiency dictates this method.
  407. // They check the queue to see when the next event is, plant
  408. // a timer entry for that time, then when it calls, bring
  409. // up the alarm panel, play the jingle, etc., handle the
  410. // anniversary events/snooze events/etc.
  411.  
  412. - alarmStart:sender
  413. {
  414.     Event *ev;
  415.  
  416.     #ifdef DEBUG
  417.         fprintf(stderr,"Alarm starting\n");
  418.     #endif
  419.     
  420.     // Wake up an alarm is starting
  421.     [NXApp activateSelf: NO];    // Let's activate ourself!    
  422.     [Sound getVolume: &oldVolumeLeft:&oldVolumeRight];
  423.         
  424.     ev = [Event newAt:[global eventFile]];
  425.     [ev firstEvent];            // Read the first event
  426.  
  427.     if( [alarmPanel isVisible])    // If it is visible
  428.         {            // then let us just 
  429.         if( [ev playAlarm] && alarmFree && ![ev runCommand])
  430.              // play the alarm sound
  431.             [self playAlarm: [ev alarmSound]]; 
  432.         
  433.         [[self stopAlarmTimedEntry] startAlarmTimedEntry: 3*60.0];
  434.         [ev free];
  435.         return self;        // repeat after three minutes
  436.         }
  437.     
  438.     if( [ev    showMessage])    
  439.         {
  440.         [alarmPanelTime setStringValue: 
  441.             ascMyTime([ev time], NOSEC,
  442.             [global militaryTime]) at:0];
  443.         [alarmPanelMessage setStringValue : [ev message]   at:0];
  444.         [alarmPanelNextEvent setStringValue: ascMyTime( 
  445.             fixAnniversary([ev time],  [ev anniversary], 
  446.                 [ev annvSpecial]), 
  447.                 NOSEC, [global militaryTime]) at: 0];
  448.                 
  449.         if( [ev snoozeNo] > 1)
  450.             [snoozeButton setEnabled :    TRUE];
  451.         else
  452.             [snoozeButton setEnabled :    FALSE];
  453.     
  454.         [snoozeButton    setState:    0];
  455.         [okButton    setState:    0];
  456.     
  457.         [alarmPanel    makeKeyAndOrderFront: self];
  458.         NXPing();
  459.         }
  460.     
  461.     #ifdef DEBUG
  462.         fprintf(stderr, "Starting alarm with: %s\n    : %s",
  463.            ascMyTime([ev time], NOSEC, [global militaryTime]), 
  464.            [ev message]);
  465.     #endif
  466.     
  467.     if( alarmFree && [ev playAlarm] && ![ev runCommand] )
  468.         [self playAlarm: [ev alarmSound]];
  469.     else
  470.         {
  471.         #ifdef DEBUG
  472.             fprintf(stderr,"Alarm is not free or no alarm.....\n");
  473.         #endif
  474.         if( [ev runCommand] )
  475.             system( [ev alarmSound] );
  476.  
  477.         }
  478.     
  479.     [ev free];
  480.     [self nextSnoozeAlarm];    
  481.     
  482.     return self;    
  483. }
  484.  
  485. - playAlarm: (char *) alarmSound
  486. {
  487.     int error;
  488.     
  489.     // From 1.0 Docs, Sound Kit, pp22-498
  490.     [Sound getVolume: &oldVolumeLeft:&oldVolumeRight];
  491.     
  492.     #ifdef DEBUG
  493.         fprintf(stderr,"Old volume L=%f R=%f, New Volume =%f\n",
  494.             oldVolumeLeft, oldVolumeRight, [global volume]);
  495.     #endif
  496.  
  497.     alarmFree = FALSE;
  498.             
  499. //    if( index(alarmSound, '/') == (char *) 0)
  500.         soundfile = [Sound findSoundFor:  alarmSound];
  501. //    else
  502. //        soundfile = [Sound newFromSoundfile: alarmSound];
  503.  
  504.     if( soundfile != nil)
  505.         {
  506.         [soundfile setDelegate: self];
  507.         if( [global volume] >= 0)
  508.             [Sound setVolume: [global volume]: 
  509.                 [global volume]];
  510.         error = [soundfile play];
  511.         if( error != 0)                
  512.             {
  513.             fprintf(stderr,"Cassandra: "
  514.                 "There was an error %d (%s)"
  515.                 " playing the alarm sound: %s.\n", 
  516.                 error, SNDSoundError(error), 
  517.                 alarmSound);
  518.             alarmFree = TRUE;
  519.             // Restore old volume
  520.             [Sound    setVolume: oldVolumeLeft :
  521.                  oldVolumeRight];
  522.              }
  523.         }
  524.     else
  525.         {
  526.         fprintf(stderr, 
  527.             "%s: Couldn't find the alarm sound <%s>."
  528.             "Substituting with obnoxious system beep.\n",
  529.                 PROGNAME, alarmSound);
  530.         NXBeep();
  531.         alarmFree = TRUE;
  532.         }
  533.     return self;
  534. }
  535. - hadError : sender
  536. {
  537.     char errorString[200];
  538.     int error = [[sender soundBeingProcessed] processingError];    
  539.     
  540.     sprintf(errorString,
  541.         "Error #%d (%s) occured while playing the alarm sound <%s>.\n", 
  542.         error, SNDSoundError(error),
  543.         [[sender soundBeingProcessed] name]);
  544.                     
  545.     NXRunAlertPanel("Alarm Error",errorString, 
  546.             "Oh well...",NULL,NULL);
  547.  
  548.     [self nextSnoozeAlarm];
  549.      [self queueDidChange: self];
  550.  
  551.     // Restore old volume
  552.     [Sound    setVolume: oldVolumeLeft: oldVolumeRight];
  553.     alarmFree = TRUE;
  554.     [soundfile free];
  555.     
  556.     return self;
  557. }
  558.  
  559.  
  560. - didPlay: sender
  561. {        
  562.  
  563.     #ifdef DEBUG
  564.         fprintf(stderr,"Alarm Freed! Restoring volume to %f and %f\n",
  565.             oldVolumeLeft, oldVolumeRight);
  566.     #endif
  567.     
  568.     // If the alarm panel is invisible, it must mean the user
  569.     // meant for this to be a one shot deal, thus alert
  570.     // Cassandra that the queue changed so that it can update
  571.     // the alarm timers and overview window
  572.     if( ![alarmPanel isVisible])
  573.          [self queueDidChange: self];
  574.  
  575.         
  576.     [snoozeButton setEnabled :    FALSE];
  577.     alarmFree = TRUE;
  578.     [soundfile free];
  579.  
  580.     // Restore old volume
  581.     [Sound    setVolume: oldVolumeLeft: oldVolumeRight];
  582.  
  583.     return self;
  584. }
  585.  
  586. - nextSnoozeAlarm
  587. {
  588.     EFileLink here;
  589.     Event *ev;
  590.  
  591.     ev = [Event newAt:[global eventFile]];
  592.     [ev firstEvent];        /* Read the first event */
  593.  
  594.     here =  [ev present];
  595.     snooze = 0; /* Initialize it to a good number, ie. nothing */
  596.     
  597.     if( [ev snoozeNo] > 0)
  598.         {
  599.         struct tm next;
  600.             
  601.         #ifdef DEBUG
  602.             fprintf(stderr,"Snooze %d times with "
  603.                 "intervals of %d minutes.\n\n",
  604.                 [ev snoozeNo], [ev snoozeInt]);
  605.         #endif
  606.         
  607.         /* Decrease the number of snooze events by 1 */
  608.         [ev setSnoozeNo : ([ev snoozeNo] -1)];            
  609.  
  610.         /* Add the snooze interval to minutes */
  611.         /* Don't worry about overflow */
  612.         /* insertEvent can handle that */        
  613.         next = *[ev time];
  614.         next.tm_min  += [ev snoozeInt];    
  615.                                     
  616.         /* Set event parameters so that the inserted one gets nixed */
  617.         /* properly when its time is due */            
  618.         [ev setDestroy        :1];
  619.         [ev setAnniversary    :0];
  620.         [ev setPriority        :1];
  621.         [ev setTime: &next];
  622.         
  623.         /* Set the snooze marker so we know which event to nix later */
  624.         /* and insert the new event */
  625.         snooze = [ev insertEvent];
  626.         
  627.         // Turn the queueModified flag on so that the next
  628.          // timerUpdate will re-read the queue for the first event
  629.         queueModified = TRUE;
  630.  
  631.         [ev free];
  632.         }
  633.     [self alarmReset: self];
  634.     return self;
  635. }
  636.  
  637.  
  638. - alarmSnooze : sender
  639. {
  640.     #ifdef DEBUG
  641.         fprintf(stderr,"Alarm snoozeing....\n");
  642.     #endif
  643.     
  644.     if( [alarmPanel isVisible])
  645.         {
  646.         if( !alarmFree)
  647.             {
  648.             [soundfile stop];
  649.             [soundfile free];
  650.             alarmFree = TRUE;
  651.             }
  652.         [alarmPanel close];
  653.  
  654.         [self queueDidChange: self];
  655.  
  656.         // Restore old volume
  657.         [Sound    setVolume: oldVolumeLeft: oldVolumeRight];
  658.         }
  659.     return self;
  660. }
  661.  
  662. - alarmStop: sender
  663. {
  664.     if( [alarmPanel isVisible])
  665.         {
  666.         #ifdef DEBUG
  667.             fprintf(stderr,"Alarm stopping.\n");
  668.             fprintf(stderr,"Restoring volume to %f and %f\n",
  669.                 oldVolumeLeft, oldVolumeRight);
  670.         #endif
  671.  
  672.         if( !alarmFree)
  673.             {
  674.             [soundfile stop];
  675.             [soundfile free];
  676.             alarmFree = TRUE;
  677.             }
  678.         [alarmPanel close];
  679.         
  680.         // Restore old volume
  681.         [Sound    setVolume: oldVolumeLeft: oldVolumeRight];
  682.         
  683.         if( snooze > 0 )
  684.             {
  685.             id ev;
  686.         
  687.              ev = [Event newAt:[global eventFile]];
  688.             [ev deleteEvent: snooze];
  689.             [ev free];
  690.             snooze = 0;
  691.             }
  692.  
  693.         [self queueDidChange: self];
  694.         }
  695.     return self;
  696. }
  697.  
  698.  
  699. // This is connected to the Reset/Cancel Button on the main menu->
  700. // Modify Events submenu.
  701. - resetCancel : sender
  702. {
  703.     Event *ev;
  704.  
  705.     ev = [Event newAt:[global eventFile]];
  706.     [ev firstEvent];    /* Read the first event */
  707.  
  708.     if( [ev present] == 0)
  709.         {
  710.         NXRunAlertPanel("Alert",
  711.                 "You cannot reset/cancel the next event "
  712.                 "since there are no events...",
  713.                 "OK", NULL, NULL);
  714.         [ev free];
  715.         return self;
  716.         }
  717.         
  718.     if( NXRunAlertPanel(    "Alert",
  719.                 "This will reset the next event. "
  720.                 "Are you sure you want to do this?",
  721.                 "Do it","Cancel",NULL) == 1)
  722.         {
  723.         [self alarmReset:self];
  724.         [self queueDidChange: self];
  725.         }
  726.  
  727.     [ev free];
  728.     return self;
  729. }
  730.  
  731. // Actually reset the next event.
  732. // Delete the first event in the queue. Log it if it has a high
  733. // enough priority. Check if it is an anniversary event, and handle
  734. // that.
  735. - alarmReset: sender
  736. {
  737.     Event *ev;
  738.     FILE *fd;
  739.     int temp;
  740.     static    struct tm lastEventTime;
  741.     
  742.     ev = [Event newAt:[global eventFile]];
  743.  
  744.     [ev firstEvent];        /* Read the first event */
  745.     
  746.     if( [ev priority] >= [global lowPriority] && [ev present] != 0)
  747.         {
  748.         char errormsg[128];
  749.         
  750.         // Ok, we are writing to the event log, just to be sure,
  751.         // let's tell the event log object to save itself.
  752.         [eventLog save];
  753.  
  754.         sprintf(errormsg, "Fatal error while creating "
  755.             "the log file %s", [global eventLogFile]);
  756.             
  757.         // Check to make sure it exists, if it does not
  758.         // we have to create it and stick a header on it
  759.         if( access( [global eventLogFile], F_OK ) == -1)    
  760.             {
  761.             if(  ( fd = fileOpen( (char *)[global eventLogFile],
  762.                 "a", errormsg)) != NULL)
  763.                 {
  764.                 fprintf(fd, "{\\rtf0\\ansi"
  765.                     "{\\fonttbl\\f0\\fswiss "
  766.                     "Helvetica;}\n}\n");
  767.                 fclose( fd);
  768.                 }
  769.             else
  770.                 fprintf(stderr,"%s: Couldn't create logfile\n",
  771.                      PROGNAME);
  772.             }
  773.                 
  774.             
  775.         sprintf(errormsg, "Fatal error while appending to "
  776.             "the log file %s", [global eventLogFile]);
  777.             
  778.         if(  ( fd = fileOpen((char *) [global eventLogFile],"a", 
  779.             errormsg)) != NULL)
  780.             {
  781.             fseek(fd, -2, SEEK_END); // Remove bracket from end
  782.             if(     lastEventTime.tm_mday != timeNow()->tm_mday ||
  783.                 lastEventTime.tm_mon  != timeNow()->tm_mon ||
  784.                 lastEventTime.tm_year != timeNow()->tm_year)
  785.                 {
  786.                 fprintf(fd,    "\\par\\f0\\b %s\\par\n",
  787.                     ascMyDate([ev time]));
  788.                 lastEventTime = *timeNow();
  789.                 }
  790.                 
  791.             if( [ev priority] > [global highPriority])
  792.                 fprintf(fd, "\\f0\\b ");
  793.             else
  794.                 fprintf(fd, "\\f0\\b0 ");
  795.  
  796.             if( [global militaryTime])
  797.                 fprintf(fd, "\t%2d:%02d\t%s"
  798.                         "\\par\n", 
  799.                     [ev hour], [ev min],
  800.                     [ev message]);
  801.             else
  802.                 {
  803.                 temp = [ev hour];
  804.             
  805.                 if( temp > 12 )
  806.                     temp -= 12;
  807.                 if( temp == 0 )
  808.                     temp = 12;            
  809.             
  810.                 fprintf(fd, "\t%2d:%02d %s\t%s"
  811.                         "\\par\n", 
  812.                     temp,
  813.                     [ev min], 
  814.                     ([ev hour]>11)?"pm":"am",
  815.                     [ev message]);
  816.                 }
  817.             fprintf(fd, "}");
  818.             fclose(fd);
  819.             [eventLog  update];
  820.             }
  821.         else
  822.             fprintf(stderr,"couldn't open file\n");
  823.  
  824.         }
  825.     else
  826.         {
  827.         #ifdef DEBUG
  828.             fprintf(stderr,"Not high enough priority to log.\n");
  829.         #endif
  830.         }
  831.     
  832.     [ev murderEvent: [ev present]];    // Kill/delete/anniverserate event
  833.     
  834.     // Turn the queueModified flag on so that the next timerUpdate 
  835.     // will re-read the queue for the first event
  836.     [self queueDidChange: self];
  837.  
  838.     [ev free];
  839.     return self;
  840. }
  841.  
  842. // Message   queueDidChange:sender 
  843. // Received if the event queue has changed in any fashion.
  844. // Tells other objects to update themselves to respond to this change.
  845. - queueDidChange: sender
  846. {
  847.     
  848.     // Notify the overview window object that the queue has changed
  849.     [overview queueDidChange:self];
  850.     
  851.     // Notify the Today window to update itself just in case
  852.     [today update];
  853.  
  854.     // Notify the Week window to update itself just in case
  855.     [week update];
  856.     
  857.     // Update the clockView if necessary
  858.     [self updateClockViewMessage: self];
  859.     
  860.     // Turn the queueModified flag on so that the next timerUpdate 
  861.     // will re-read the queue for the first event
  862.     queueModified = TRUE;
  863.     
  864.     // Check the next alarm, now that the flag has been set.
  865.     [self timerUpdate:self];
  866.     
  867.     // And reschedule the timer entry for the next alarm.
  868.     // Give the user ten seconds to react
  869.     [[self stopAlarmTimedEntry] startAlarmTimedEntry: 10.0];
  870.     
  871.     return self;
  872. }
  873.  
  874. - updateClockViewMessage: sender
  875. {
  876.     static char buf1[80], buf2[80];
  877.     extern const char *shortMonths[12];
  878.     
  879.     //Check to see if we are using the snazzy digital clock, if we are
  880.     // then we want to snarf off the first event and pipe it to Clock
  881.     if( [global clockType] == CLOCK_DIGITAL)    
  882.         {
  883.         Event *ev;
  884.         int hour;
  885.         char *suffix;
  886.         
  887.         ev = [Event newAt:[global eventFile]];    
  888.         [ev firstEvent];    // Read in the first event 
  889.  
  890.         hour = [ev hour];
  891.         if( ![global militaryTime])
  892.             {            
  893.             if( hour >= 12 )
  894.                 {
  895.                 suffix = "p";
  896.                 hour -= 12;
  897.                 }
  898.             else
  899.                 suffix = "a";
  900.                 
  901.             if( hour == 0 )
  902.                 hour = 12;            
  903.             }
  904.         else
  905.             suffix = "";
  906.         
  907.         sprintf(buf1,"%2d:%02d%s %s %d", hour, [ev min], suffix, 
  908.                 shortMonths[ [ev mon] ], [ev mday]);
  909.         
  910.         strncpy(buf2, [ev message], 20);
  911.         [clockView setMessage:     buf1: buf2];
  912.         [ev free];
  913.         }
  914.     return self;
  915. }
  916.  
  917. - defaultsDidChange: sender
  918. {
  919.     const char *str;
  920.  
  921.     #ifdef DEBUG
  922.         fprintf(stderr, 
  923.             "Defaults did change... App compensating.\n");
  924.     #endif
  925.  
  926.     [overview     defaultsDidChange: self];
  927.     [today        defaultsDidChange: self];
  928.     [week        defaultsDidChange: self];
  929.   
  930.     // Read the ShowSeconds default and process it.
  931.     [clockView setShowSecondsToBool: [global showSeconds]];
  932.     [clockView setShowDateToBool: [global showDate]];
  933.     [clockView setClockTypeToInt: [global clockType]];
  934.     [clockView setMilitaryTimeToBool: [global militaryTime]];
  935.     [self updateClockViewMessage: self];
  936.         
  937.     // Read the FaceBitmap default and process that.
  938.     str = [global clockfaceBitmapName];
  939.     [clockView setClockFaceToBitmapNamed:(str && str[0] ? str : NULL)];
  940.  
  941.     saveScreen = [global saveScreen];
  942.     
  943.     return self;
  944. }
  945.  
  946.  
  947. //  Message timerUpdate: sender
  948. // This checks the present alarm and the first event of the queue and compares
  949. // them. If the event at the very front is equal to or greater 
  950. // than the present time, then send off messages turning on the alarm system.
  951. - timerUpdate :sender
  952. {
  953.     static struct tm nextAlarmTime;
  954.     static int presentEventNumber;
  955.     static BOOL backlog = NO;
  956.     
  957.     // Check to see if the queue has been modified/changed since the last
  958.     // time we read. If it has, we have to re-read the first event in
  959.     // since its time may have changed.
  960.     if( queueModified)
  961.         {
  962.         Event *ev;
  963.  
  964.         ev = [Event newAt:[global eventFile]];    
  965.         
  966.         #ifdef DEBUG
  967.             fprintf(stderr,"Queue modified/new. "
  968.                 "Re-reading event.\n");
  969.         #endif
  970.  
  971.         [ev firstEvent];    // Read in the first event 
  972.         
  973.         /* Remember its number (place) in the queue */
  974.         presentEventNumber = [ev present];
  975.         
  976.         /* and copy down the time at which it will occur */
  977.         nextAlarmTime = *[ev time];    
  978.         
  979.         queueModified = FALSE;        // Reset the flag,
  980.         [ev free];            // and tidy up
  981.         }
  982.     else
  983.         {
  984.         // If the list was not modified, we can stick with the 
  985.         // information we
  986.         // already have in presentEventNumber and nextAlarmTime 
  987.         #ifdef DEBUG
  988.             fprintf(stderr, 
  989.                 "Queue not modified. Skipping re-read.\n");
  990.         #endif
  991.         }
  992.  
  993.     /* How many more seconds until the event? */
  994.     if( presentEventNumber > 0)  // Check if we're on the first event
  995.         secondsUntilEvent =  secondsBetween(    (struct tm *) 
  996.         timeNow(), &nextAlarmTime    );
  997.     else
  998.         secondsUntilEvent = 60*60*4;    // Four hours
  999.     
  1000.     #ifdef DEBUG
  1001.         fprintf(stderr, "Time now               = %s.\n", 
  1002.               ascMyTime((struct tm*) timeNow(),
  1003.                  SEC, [global militaryTime]));
  1004.         fprintf(stderr, "Event #%d's time = %s. \n"
  1005.             "secondsUntilEvent = %6.1f sec. (%6.1f min.)\n",
  1006.             presentEventNumber,
  1007.             ascMyTime( &nextAlarmTime,SEC, [global militaryTime]),
  1008.             secondsUntilEvent, secondsUntilEvent/60);
  1009.     #endif
  1010.  
  1011.     /* If it looks like the user has backlogged a lot of events, ask */
  1012.     /* her or him if she or he wants to clear all of the until today */
  1013.     if(secondsUntilEvent < (double) -(24*60*60) && presentEventNumber >0)
  1014.         {
  1015.         [NXApp unhide: self];
  1016.         if(backlog == YES || NXRunAlertPanel("Backlog Warning",
  1017.                 "You have backlogged more than a day's "
  1018.                 "worth of events. "
  1019.                 "Would you rather clear all of them?",
  1020.                 "Yes","No",NULL) == 1)
  1021.             {
  1022.             Event *ev;
  1023.             ev = [Event newAt:[global eventFile]];    
  1024.             
  1025.             backlog = YES;
  1026.             
  1027.             do
  1028.                 {
  1029.                 [self alarmReset:self];
  1030.                 [ev firstEvent];
  1031.                 }
  1032.             while(  secondsBetween((struct tm *) timeNow(),
  1033.                  [ev time]) < 0 && [ev present] > 0);
  1034.              
  1035.             [ev free];
  1036.             [self queueDidChange:self];
  1037.             return self;
  1038.             }
  1039.         else
  1040.             backlog = NO;
  1041.         }
  1042.                 
  1043.  
  1044.     /* If the event's time has come and it isn't a bogus event */
  1045.     /* (0 or below are ignored) then start the alarm */
  1046.     if(   secondsUntilEvent <=  0 &&  presentEventNumber > 0)
  1047.         {
  1048.         [self alarmStart:self];
  1049.         }
  1050.     else
  1051.         {
  1052.         // OK, then let us start the bloody timer again
  1053.         [self startAlarmTimedEntry: secondsUntilEvent];
  1054.         }
  1055.     return self;
  1056. }
  1057.  
  1058. // ===================================
  1059. //         Timer Routines
  1060. // ===================================
  1061. // These handle the timer object
  1062. // for alarm control.
  1063. // Borrowed mostly off the Animator object.
  1064.  
  1065. void checkAlarm(teNum, now, cassandra) 
  1066. DPSTimedEntry teNum;
  1067. double now;
  1068. id cassandra;
  1069. {
  1070.     [cassandra stopAlarmTimedEntry];
  1071.     [cassandra timerUpdate:cassandra];
  1072. }
  1073.  
  1074.  
  1075. - startAlarmTimedEntry:(double) fireWhen
  1076. {
  1077.     double fireIn;
  1078.  
  1079.     if( fireWhen > 0.0)
  1080.         fireIn =  fireWhen;
  1081.     else
  1082.         {
  1083.         if (fireWhen = NOW || fireWhen < 0.0) 
  1084.             fireIn = 0.0;      // Fire as soon as possible!
  1085.         else
  1086.             {
  1087.              fireIn = secondsUntilEvent;
  1088.              if( fireIn > MAXSECONDS )                                                     
  1089.                  fireIn = (double) MAXSECONDS;    
  1090.             }
  1091.         }
  1092.               
  1093.     #ifdef DEBUG
  1094.         fprintf(stderr,"Starting alarmTE in %5.2f seconds. "
  1095.                 "(%d min. %d sec.)\n", fireIn,
  1096.                 ((int) fireIn / 60),  ((int)fireIn % 60));
  1097.     #endif
  1098.     
  1099.     alarmTE = DPSAddTimedEntry(fireIn, &checkAlarm, 
  1100.                     self, NX_MODALRESPTHRESHOLD);
  1101.  
  1102.     return self;
  1103. }
  1104.  
  1105. - stopAlarmTimedEntry
  1106. {
  1107.     #ifdef DEBUG
  1108.         fprintf(stderr,"Stopping alarmTE.\n");
  1109.     #endif
  1110.     if( alarmTE != NULL)
  1111.         DPSRemoveTimedEntry (alarmTE);
  1112.     
  1113.     alarmTE = NULL;
  1114.     return self;
  1115. }
  1116.  
  1117. - tick:sender;
  1118. {
  1119.     int ad =0;
  1120.     int evs;
  1121.     static dimmed = FALSE;
  1122.     static minuteNow;
  1123.     
  1124.     [overview timeUpdate:self];
  1125.     [today    timeUpdate: self];
  1126.     
  1127.     // Check things that only needed to be checked periodically.
  1128.     if( minuteNow != timeNow()->tm_min)
  1129.         {
  1130.         
  1131.         if(  timeNow()->tm_mday != now.tm_mday)
  1132.             {
  1133.             [today update];
  1134.             [overview monthUpdate: self];
  1135.             [week update];
  1136.             now = *timeNow();
  1137.             }
  1138.  
  1139.  
  1140.         if( saveScreen )
  1141.             {
  1142.             // Check state of autodimming
  1143.             evs = open( "/dev/evs0", O_RDONLY);
  1144.             ioctl(evs, EVSIOCADS, &ad);
  1145.             close(evs);
  1146.     
  1147.             if( ad )
  1148.                 {
  1149.                 if( dimmed == FALSE)
  1150.                     [self screenDimmed: self];
  1151.                 dimmed = TRUE;
  1152.                 }
  1153.             else
  1154.                 {
  1155.                 if( dimmed == TRUE)
  1156.                     [self screenUndimmed: self];
  1157.                 dimmed = FALSE;
  1158.                 }
  1159.             }
  1160.         }
  1161.     return self;
  1162. }
  1163.  
  1164. // The screen has dimmed. Call whatever program the user wants.
  1165. - screenDimmed: sender
  1166. {
  1167.     static char command[128];
  1168.     
  1169.     sprintf(command, "%s&", [global screenSaver]);
  1170.     
  1171.     #ifdef DEBUG
  1172.         fprintf(stderr,"%s: System autodimmed w/ <%s>\n", 
  1173.             PROGNAME, [global screenSaver]);
  1174.         NXBeep(); NXBeep();
  1175.     #endif
  1176.     
  1177.     system( command );
  1178.     return self;
  1179. }
  1180.  
  1181. - screenUndimmed: sender
  1182. {
  1183.     #ifdef DEBUG
  1184.         fprintf(stderr,"%s: Screen undimmed.\n", PROGNAME);
  1185.     #endif
  1186.     
  1187.     return self;
  1188. }
  1189.  
  1190. - global    { return global; }
  1191. @end
  1192.  
  1193.